package com.paul.web.exception;

import com.paul.common.base.II18nService;
import com.paul.common.bean.ApiResult;
import com.paul.common.exception.BusinessException;
import com.paul.common.exception.ExceptionCode;
import com.paul.common.utils.HttpContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Base64;
import java.util.Objects;

/**
 * @author ：zmk
 * @date ：Created in 2021/11/12 14:11
 * @description：
 */
@RestControllerAdvice
public class ExceptionController {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class);
    private final Base64.Encoder encoder = Base64.getEncoder();
    private final II18nService ii18nService;

    @Autowired
    public ExceptionController(II18nService ii18nService) {
        this.ii18nService = ii18nService;
    }

    // 捕捉控制器里面自己抛出的所有异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResult> globalException(Exception e) {
        logger.error("全局异常：{}", e.getStackTrace());
        HttpServletResponse response = HttpContextUtil.getResponse();
        StringWriter stringWriter = new StringWriter();
        try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
            e.printStackTrace(printWriter);
            printWriter.flush();
            stringWriter.flush();
            String strError = stringWriter.toString().substring(0, 2000);
            Objects.requireNonNull(response).setHeader("error", encoder.encodeToString(strError.getBytes())); //对异常信息进行base64编码
        } catch (Exception ex) {
            logger.error("全局异常=> 方法：{}", ex.getStackTrace());
        }
        String msg = ii18nService.getMessage("i18n.key.common.system.error");
        return new ResponseEntity<>(
                new ApiResult<>(
                        HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, null), HttpStatus.INTERNAL_SERVER_ERROR
        );
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<ApiResult> handleException(AccessDeniedException e) {
//        System.out.println("执行ExceptionController的handleException方法,异常类型：AccessDeniedException");
        return new ResponseEntity<>(
                new ApiResult<>(
                        HttpStatus.FORBIDDEN.value(), e.getMessage(), null), HttpStatus.FORBIDDEN
        );
    }

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ApiResult> handleException(BusinessException e) {
//        System.out.println("执行ExceptionController的handleException方法,异常类型：BusinessException");
        return new ResponseEntity<>(
                new ApiResult<>(
                        e.getCode(), e.getMsg(), e.getData()), HttpStatus.OK
        );
    }

    /**
     * 处理空指针的异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({NullPointerException.class})
    public ApiResult nullPointerException(NullPointerException ex) {
        return printExceptionMessage(ii18nService.getMessage("i18n.key.common.param.mandatory"), "NullPointerException", ex);
    }

    /**
     * 参数类型不匹配
     * getPropertyName()获取数据类型不匹配参数名称
     * getRequiredType()实际要求客户端传递的数据类型
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({TypeMismatchException.class})
    public ApiResult requestTypeMismatch(TypeMismatchException ex) {
        return printExceptionMessage(ii18nService.getMessage("i18n.key.common.param.notTyre") + ex.getPropertyName() + ii18nService.getMessage("i18n.key.common.param.typeShould") + ex.getRequiredType(), "TypeMismatchException", ex);
    }


    /**
     * 缺少参数异常
     * getParameterName() 缺少的参数名称
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ApiResult requestMissingServletRequest(MissingServletRequestParameterException ex) {
        return printExceptionMessage(ii18nService.getMessage("i18n.key.common.param.lack") + ex.getParameterName(), "MissingServletRequestParameterException", ex);
    }

    /**
     * 处理 SpringMVC 参数校验不正确
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
        FieldError fieldError = ex.getBindingResult().getFieldError();
        assert fieldError != null; // 断言，避免告警
        return printExceptionMessage( String.format("请求参数不正确:%s", fieldError.getDefaultMessage()),"MethodArgumentNotValidException",ex);
    }

    /**
     * 处理 SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验
     */
    @ExceptionHandler(BindException.class)
    public ApiResult bindExceptionHandler(BindException ex) {
        FieldError fieldError = ex.getFieldError();
        assert fieldError != null; // 断言，避免告警
        return printExceptionMessage( String.format("请求参数不正确:%s", fieldError.getDefaultMessage()),"bindExceptionHandler",ex);
    }

    /**
     * 处理 Validator 校验不通过产生的异常
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ApiResult constraintViolationExceptionHandler(ConstraintViolationException ex) {
        ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().iterator().next();
        return printExceptionMessage( String.format("请求参数不正确:%s", constraintViolation.getMessage()),"constraintViolationExceptionHandler",ex);
    }


    private ApiResult printExceptionMessage(String message, String exceptionName, Exception e) {
        logger.error(" 请求过程中发生异常,异常名称={},异常信息={}", exceptionName, e);
        return new ApiResult(ExceptionCode.PARAMETER_ERROR.value(), message);
    }
//    private static String getStackTrace(Throwable t) {
//        ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream();
//        t.printStackTrace(new java.io.PrintWriter(buf, true));
//        return buf.toString();
//    }
}